use std::ops::{Add, Sub};

pub struct ByteCursor {
    data: Vec<u8>,
    pos: usize,
}

impl ByteCursor {
    pub fn new(data: Vec<u8>) -> Self {
        ByteCursor {
            data,
            pos: 0,
        }
    }

    pub fn set_pos(&mut self, pos: usize) {
        self.pos = pos;
    }

    pub fn get_pos(&self) -> usize {
        self.pos
    }

    pub fn read_one(&mut self) -> Option<u8> {
        if self.pos < self.data.len() {
            let result = self.data[self.pos];
            self.pos += 1;
            Some(result)
        } else {
            None
        }
    }

    pub fn read(&mut self, len: usize) -> Option<&[u8]> {
        if self.pos + len <= self.data.len() {
            let result = &self.data[self.pos..self.pos + len];
            self.pos += len;
            Some(result)
        } else {
            None
        }
    }

    pub fn get_one(&self) -> Option<u8> {
        if self.pos < self.data.len() {
            Some(self.data[self.pos])
        } else {
            None
        }
    }

    pub fn get(&self, len: usize) -> Option<&[u8]> {
        if self.pos + len <= self.data.len() {
            Some(&self.data[self.pos..self.pos + len])
        } else {
            None
        }
    }

    pub fn left_parts(&self) -> &[u8] {
        &self.data[self.pos..]
    }
}

impl Add<usize> for ByteCursor {
    type Output = Self;

    fn add(mut self, other: usize) -> Self::Output {
        self.pos = self.pos.saturating_add(other);
        self
    }
}

impl Sub<usize> for ByteCursor {
    type Output = Self;

    fn sub(mut self, other: usize) -> Self::Output {
        self.pos = self.pos.saturating_sub(other);
        self
    }
}

